package modbus

import (
	"errors"
	"fmt"

	"gitee.com/lossage/simplego/io"
	"gitee.com/lossage/simplego/strings"
)

type ModbusTCP struct {
	FrameListener
	SenderHeader  ModbusTCPHeader
	ReceiveHeader ModbusTCPHeader
	modbusAction  IModbusAction
	tcpClient     io.IConnectionClient
}

func CreateModbusTCP(ip string, port, timeOutMilsecond int, autoReconnect bool) (IModbusTask, error) {
	res := new(ModbusTCP)
	res.modbusAction = CreateModbusAction()
	res.SenderHeader.TxNo = 0
	res.ReceiveHeader.TxNo = 0
	client, err := io.CreateTCPClient(ip, port, timeOutMilsecond, autoReconnect)
	res.tcpClient = client
	if err != nil {
		return nil, err
	}
	return res, res.tcpClient.Connect()
}

func (_this *ModbusTCP) ReadOnce(adress, command uint8, startReg uint16, regLen uint8, datas []uint16) (*ModbusModel, *ModbusModel, error) {
	if _this.tcpClient == nil {
		return &_this.modbusAction.GetReceiveFrame().ModbusModel, &_this.modbusAction.GetSendFrame().ModbusModel, errors.New("tcp连接为空")
	}
	if _this.tcpClient.IsConnecting() {
		return &_this.modbusAction.GetReceiveFrame().ModbusModel, &_this.modbusAction.GetSendFrame().ModbusModel, errors.New("正在连接中")
	}
	writer := func(buffer []byte) error {
		_this.SenderHeader.TxNo = _this.SenderHeader.TxNo + 1
		_this.SenderHeader.RxNo = _this.ReceiveHeader.TxNo
		_this.SenderHeader.Length = uint16(len(buffer))
		headerBuffer := _this.SenderHeader.ToArray()
		buffer = append(headerBuffer, buffer...)
		fmt.Println("send:" + strings.ArrayToHexString(buffer))
		if _this.senderEvent != nil {
			_this.senderEvent.Event(buffer)
		}
		return _this.tcpClient.Write(buffer)
	}
	reader := func() ([]byte, error) {
		buffer, err := _this.tcpClient.Read()
		if err != nil {
			return nil, err
		}
		if _this.receiverEvent != nil {
			_this.receiverEvent.Event(buffer)
		}
		_this.ReceiveHeader.FromArray(buffer[:6])
		fmt.Println("receive:" + strings.ArrayToHexString(buffer))
		return buffer[6:], nil
	}
	_this.modbusAction.SetWriter(&writer)
	_this.modbusAction.SetReader(&reader)
	err := _this.modbusAction.ReadOnce(adress, command, startReg, regLen)
	return &_this.modbusAction.GetReceiveFrame().ModbusModel, &_this.modbusAction.GetSendFrame().ModbusModel, err
}
